/**
 * Redo the program in Figure 15.33 using the XSI semaphore functions from Section 15.8 to
 * alternate between the parent and the child.
 **/
#include "apue.h"

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/sem.h>

#define NLOOPS 1000
#define SIZE sizeof(long) /* size of shared memory area */

static int update(long *ptr)
{
  return ((*ptr)++); /* return value before increment */
}

int main(void)
{
  int fd, i, counter, id;
  pid_t pid;
  void *area;

  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
  } arg;
  unsigned short arr[2];
  struct sembuf buf[1] = {0};

  if ((fd = open("/dev/zero", O_RDWR)) < 0)
    err_sys("open error");
  if ((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
    err_sys("mmap error");
  close(fd); /* can close /dev/zero now that it's mapped */

  if ((id = semget(IPC_PRIVATE, 2, IPC_CREAT | S_IWUSR | S_IRUSR)) < 0) {
    err_sys("semget error");
  }

  // 信号量初值为0
  arr[0] = 0; /* 父进程使用 */
  arr[1] = 0; /* 子进程使用 */
  arg.array = arr;
  if (semctl(id, 0, SETALL, arg) == -1) {
    err_sys("semctl error");
  }

  if ((pid = fork()) < 0) {
    err_sys("fork error");
  } else if (pid > 0) { /* parent */
    for (i = 0; i < NLOOPS; i += 2) {
      if ((counter = update((long *)area)) != i)
        err_quit("parent: expected %d, got %d", i, counter);
      else
        printf("parent: %d\n", i);

      buf[0].sem_num = 1; /* 给子进程信号量 */
      buf[0].sem_op = 1;  /* 释放信号量 */
      buf[0].sem_flg = 0;
      if (semop(id, buf, 1) == -1) {
        err_sys("semop error");
      }

      buf[0].sem_num = 0; /* 等待父进程信号量 */
      buf[0].sem_op = -1; /* 获取信号量 */
      buf[0].sem_flg = 0;
      if (semop(id, buf, 1) == -1) {
        err_sys("semop error");
      }
    }
  } else { /* child */
    for (i = 1; i < NLOOPS + 1; i += 2) {
      buf[0].sem_num = 1; /* 等待子进程信号量 */
      buf[0].sem_op = -1; /* 获取信号量 */
      buf[0].sem_flg = 0;
      if (semop(id, buf, 1) == -1) {
        err_sys("semop error");
      }

      if ((counter = update((long *)area)) != i)
        err_quit("child: expected %d, got %d", i, counter);
      else
        printf("child : %d\n", i);

      buf[0].sem_num = 0; /* 给父进程信号量 */
      buf[0].sem_op = 1;  /* 释放信号量 */
      buf[0].sem_flg = 0;
      if (semop(id, buf, 1) == -1) {
        err_sys("semop error");
      }
    }
  }

  exit(0);
}
